class_name GameplayEffectManager

##
signal effect_applied(effect: GameplayEffect)

##
signal effect_removed(effect: GameplayEffect)

##
var owner: AbilitySystemComponent

##
var effect_map: Dictionary = {}

##
var affected_attribute_map: Dictionary = {}


func _init(init_owner: AbilitySystemComponent) -> void:
	owner = init_owner


func apply_effect(effect_asset: GameplayEffectAsset, source: AbilitySystemComponent) -> void:
	if owner.tag_manager.has_any_tags(effect_asset.tags_cancelled_effect):
		return
	
	if effect_asset.stack_policy == GameplayEffectAsset.StackPolicy.None and\
	   effect_map.has(effect_asset.name):
		return

	var effect = effect_map.get_or_add(effect_asset.name, effect_asset.create_spec(source, owner)) as GameplayEffect

	# 堆叠
	if effect_asset.stack_policy != GameplayEffectAsset.StackPolicy.None:
		effect.stack_count += 1
	
	# 持续时间刷新
	if effect_asset.duration_refresh_policy == GameplayEffectAsset.DurationRefreshPolicy.RefreshOnSuccessfulApplication:
		effect.reset_duration()
	
	# 效果触发
	if effect_asset.duration_policy == GameplayEffectAsset.DurationPolicy.Instant:
		effect.execute_effect()
	else:
		effect.on_attach()

		affected_attribute_map.get_or_add(effect_asset.attribute_name, []).append(effect)
		
		_on_effect_changed(effect_asset.attribute_name)
		
		effect_applied.emit(effect)


func apply_effects(effect_assets: Array[GameplayEffectAsset], source: AbilitySystemComponent) -> void:
	for effect_asset: GameplayEffectAsset in effect_assets:
		apply_effect(effect_asset, source)


func remove_effect(effect_asset: GameplayEffectAsset) -> void:
	var effect = effect_map.get(effect_asset.name)
	if effect:
		effect.on_detach()

		effect_map.erase(effect_asset.name)
		affected_attribute_map.get(effect_asset.attribute_name, []).erase(effect)
		
		_on_effect_changed(effect_asset.attribute_name)

		effect_removed.emit(effect)


func remove_effects(effect_assets: Array[GameplayEffectAsset]) -> void:
	for effect_asset: GameplayEffectAsset in effect_assets:
		remove_effect(effect_asset)


func activate_effect(effect_asset: GameplayEffectAsset) -> void:
	var effect = effect_map.get(effect_asset.name)
	if effect:
		effect.active = true
		
		_on_effect_changed(effect_asset.attribute_name)


func activate_effects(effect_assets: Array[GameplayEffectAsset]) -> void:
	for effect_asset: GameplayEffectAsset in effect_assets:
		activate_effect(effect_asset)


func deactivate_effect(effect_asset: GameplayEffectAsset) -> void:
	var effect = effect_map.get(effect_asset.name)
	if effect:
		effect.active = false
		
		_on_effect_changed(effect_asset.attribute_name)


func deactivate_effects(effect_assets: Array[GameplayEffectAsset]) -> void:
	for effect_asset: GameplayEffectAsset in effect_assets:
		deactivate_effect(effect_asset)


func on_update(delta: float) -> void:
	var expired_effects: Array[GameplayEffect] = []
	
	for effect: GameplayEffect in effect_map.values():
		if effect.is_expired:
			expired_effects.append(effect)
		else:
			effect.on_update(delta)
	
	for effect: GameplayEffect in expired_effects:
		remove_effect(effect.effect_asset)


func _on_effect_changed(modified_attribute_name: String) -> void:
	var attribute := owner.attribute_set.get_attribute(modified_attribute_name)
	attribute.current_value = attribute.base_value
	
	for effect: GameplayEffect in affected_attribute_map.get(modified_attribute_name, []):
		# 周期触发的效果在应用时不结算效果
		if effect.effect_asset.period == 0.0:
			effect.execute_effect()
